package com.ease.platform.access.service.impl;

import static com.ease.dao.util.DataAccessObjectUtil.toParameters;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.ease.common.util.Assertion;
import com.ease.common.vo.MapParameter;
import com.ease.common.vo.Ordering;
import com.ease.common.vo.Pagination;
import com.ease.dao.DataAccessObjectConst;
import com.ease.dao.IbatisDataAccessObject;
import com.ease.dao.util.DataAccessObjectUtil;
import com.ease.dao.vo.PaginationMapParameter;
import com.ease.model.Model;
import com.ease.model.criteria.CriteriaParameter;
import com.ease.platform.access.callback.BatchAccessCallback;
import com.ease.platform.access.callback.GroupingAccessCallback;
import com.ease.platform.access.service.AbstractParameterizedBeanIbatisAccessService;

/**
 * 
 * @author nathanleewei
 * 
 */
public class AbstractParameterizedBeanIbatisAccessServiceImpl<T extends Model> extends AbstractParameterizedBeanAccessServiceImpl<T> implements AbstractParameterizedBeanIbatisAccessService<T> {

	private IbatisDataAccessObject ibatisDataAccessObject;

	protected T findOne(String parameterName, long parameter) {
		MapParameter mapParameter = new MapParameter();
		mapParameter.put(parameterName, parameter);
		return this.findOne(mapParameter);
	}

	protected List<T> find(String parameterName, long parameter) {
		MapParameter mapParameter = new MapParameter();
		mapParameter.put(parameterName, parameter);
		return this.find(mapParameter);
	}

	public long sequence() {
		return this.getIbatisDataAccessObject().sequence(getNamespace());
	}

	public T create(T model) {
		beforeCreate(model);
		return (T) this.getIbatisDataAccessObject().create(this.getNamespace(), model);
	}

	public T update(T model) {
		beforeUpdate(model);
		return (T) this.getIbatisDataAccessObject().update(this.getNamespace(), model);
	}

	/** remove */
	public T remove(T model) {
		updateLifecycleAttribute(model);
		// TODO:bak
		return (T) this.getIbatisDataAccessObject().remove(this.getNamespace(), model);
	}

	public List<T> saveList(List<T> models) {
		if (notEmpty(models)) {
			for (T model : models) {
				save(model);
			}
		}
		return models;
	}

	public T save(T model) {
		Assertion.notNull(model, "model not found");
		if (model.beNew()) {
			this.create(model);
		} else {
			this.update(model);
		}
		return model;
	}

	public List<T> removeList(List<T> models) {
		if (notEmpty(models)) {
			for (T model : models) {
				remove(model);
			}
		}
		return models;
	}

	public void save(List<T> parameterModels, BatchAccessCallback<T> batchAccessCallback) {

		if (notEmpty(parameterModels)) {

			List<T> entityModels = batchAccessCallback.find();
			T entityModel = null;

			for (T parameterModel : parameterModels) {
				if (parameterModel.beNew()) {
					if (batchAccessCallback.isCreationEnabled(parameterModel)) {
						this.create(parameterModel);
					} else {
						// do nothing
					}
				} else {
					entityModel = DataAccessObjectUtil.getById(parameterModel.getId(), entityModels);
					Assertion.notNull(entityModel, "entityModel not found, model.id=" + parameterModel.getId());
					batchAccessCallback.copryAttribute(entityModel, parameterModel);
					entityModel.setVersion(parameterModel.getVersion());
					this.update(entityModel);
				}
			}
		}
	}

	/** create */
	public int create(List<T> models) {
		if (notEmpty(models)) {
			for (T model : models) {
				createLifecycleAttribute(model);
				updateLifecycleAttribute(model);
			}
		}
		return this.getIbatisDataAccessObject().create(this.getNamespace(), models);
	}

	/** retrieve/read/find */
	@SuppressWarnings("unchecked")
	public T findById(long id) {
		Assertion.isPositive(id, "id is not positive, id=" + id);
		return (T) this.getIbatisDataAccessObject().findById(this.getNamespace(), id);
	}

	/** retrieve/read/find */
	public List<T> findByIds(long[] ids) {

		List<T> models = null;

		if (notEmpty(ids)) {

			/* switch between findByIds and for_each{findById} */
			if (isFindByIdsUsingInClause()) {
				return groupingAccess(ids, new GroupingAccessCallback<T>() {
					public List<T> accessGroup(long[] parameterGroup) {
						MapParameter parameter = new MapParameter();
						parameter.put(Model.Attribute_ids, parameterGroup);
						return AbstractParameterizedBeanIbatisAccessServiceImpl.this.find(DataAccessObjectConst.STATEMENT_FIND_BY_IDS, parameter);
					}
				});
			} else {
				/* escape duplicated value */
				long[] escapeDuplicationIds = deduplicate(ids);

				T model = null;
				for (long id : escapeDuplicationIds) {
					model = this.findById(id);
					if (null != model) {
						if (null == models) {
							models = new ArrayList<T>();
						}
						models.add(model);
					}
				}
			}
		}
		return escapeEmpty(models);
	}

	@SuppressWarnings("unchecked")
	public T findOne(final String statement, Map<String, Object> parameter) {
		return (T) this.getIbatisDataAccessObject().findOne(this.getNamespace(), statement, parameter);
	}

	public T findOne(final String statement, CriteriaParameter parameter) {
		return findOne(statement, toParameters(parameter));
	}

	public T findOne(CriteriaParameter parameter) {
		return this.findOne(toParameters(parameter));
	}

	public T findOne(Map<String, Object> parameter) {
		return this.findOne(DataAccessObjectConst.STATEMENT_FIND, parameter);
	}

	/* find */
	public List<T> find(final String statement, Map<String, Object> parameter, Ordering ordering) {
		return this.getIbatisDataAccessObject().find(this.getNamespace(), statement, parameter, ordering);
	}

	public List<T> find(final String statement, CriteriaParameter parameter, Ordering ordering) {
		return find(statement, toParameters(parameter), ordering);
	}

	public List<T> find(Map<String, Object> parameter, Ordering ordering) {
		return this.find(DataAccessObjectConst.STATEMENT_FIND, parameter, ordering);
	}

	public List<T> find(CriteriaParameter parameter, Ordering ordering) {
		return this.find(toParameters(parameter), ordering);
	}

	public List<T> find(final String statement, Map<String, Object> parameter) {
		return this.find(statement, parameter, (Ordering) null);
	}

	public List<T> find(final String statement, CriteriaParameter parameter) {
		return this.find(statement, toParameters(parameter), (Ordering) null);
	}

	public List<T> find(Map<String, Object> parameter) {
		return this.find(DataAccessObjectConst.STATEMENT_FIND, parameter);
	}

	public List<T> find(CriteriaParameter parameter) {
		return this.find(toParameters(parameter));
	}

	public List<T> find(String statement, Map<String, Object> parameters, int skipResult, int maxResult, Ordering ordering) {
		return this.getIbatisDataAccessObject().find(this.getNamespace(), statement, new PaginationMapParameter(parameters, ordering, skipResult, maxResult), ordering);
	}

	public List<T> find(String statement, CriteriaParameter parameter, int skipResult, int maxResult, Ordering ordering) {
		return this.find(statement, toParameters(parameter), skipResult, maxResult, ordering);
	}

	public List<T> find(Map<String, Object> parameters, int skipResult, int maxResult, Ordering ordering) {
		return this.find(DataAccessObjectConst.STATEMENT_FIND, parameters, skipResult, maxResult, ordering);
	}

	public List<T> find(CriteriaParameter parameter, int skipResult, int maxResult, Ordering ordering) {
		return this.find(DataAccessObjectConst.STATEMENT_FIND, toParameters(parameter), skipResult, maxResult, ordering);
	}

	public List<T> find(Map<String, Object> parameters, int skipResult, int maxResult) {
		return this.find(parameters, skipResult, maxResult, null);
	}

	public List<T> find(CriteriaParameter parameter, int skipResult, int maxResult) {
		return this.find(toParameters(parameter), skipResult, maxResult);
	}

	public Pagination<T> findPagination(final String statement, Map<String, Object> parameter, Ordering ordering, final Pagination<T> pagination) {
		return this.getIbatisDataAccessObject().findPagination(this.getNamespace(), statement, parameter, ordering, pagination);
	}

	public Pagination<T> findPagination(String statement, CriteriaParameter parameter, Ordering ordering, Pagination<T> pagination) {
		return this.findPagination(statement, toParameters(parameter), ordering, pagination);
	}

	public Pagination<T> findPagination(Map<String, Object> parameter, Ordering ordering, Pagination<T> pagination) {
		return this.findPagination(DataAccessObjectConst.STATEMENT_FIND, parameter, ordering, pagination);
	}

	// Added by honghui on 2011/06/29
	public Pagination<T> findPagination(Map<String, Object> parameter, Ordering ordering, Pagination<T> pagination, int skipResult, int maxResult) {
		return this.getIbatisDataAccessObject().findPagination(this.getNamespace(), DataAccessObjectConst.STATEMENT_FIND, new PaginationMapParameter(parameter, ordering, skipResult, maxResult),
				ordering, pagination);
	}

	public Pagination<T> findPagination(CriteriaParameter parameter, Ordering ordering, Pagination<T> pagination) {
		return this.findPagination(toParameters(parameter), ordering, pagination);
	}

	// Added by honghui on 2011/06/29
	public Pagination<T> findPagination(CriteriaParameter parameter, Ordering ordering, final Pagination<T> pagination, int skipResult, int maxResult) {
		return this.findPagination(toParameters(parameter), ordering, pagination, skipResult, maxResult);
	}

	public Pagination<T> findPagination(Map<String, Object> parameter, Pagination<T> pagination) {
		return this.findPagination(parameter, null, pagination);
	}

	public Pagination<T> findPagination(CriteriaParameter parameter, Pagination<T> pagination) {
		return this.findPagination(parameter, null, pagination);
	}

	public Pagination<T> findPagination(Ordering ordering, Pagination<T> pagination) {
		return this.findPagination((MapParameter) null, ordering, pagination);
	}

	public Pagination<T> findPagination(Pagination<T> pagination) {
		return this.findPagination((Ordering) null, pagination);
	}

	public Pagination<T> findPagination() {
		return this.findPagination(null);
	}

	/** find */
	public List<T> findAll(Ordering ordering) {
		return this.getIbatisDataAccessObject().findAll(this.getNamespace(), ordering);
	}

	public List<T> findAll() {
		return this.findAll(null);
	}

	/** count */
	public int count(final String statement, Map<String, Object> parameter) {
		return this.getIbatisDataAccessObject().count(this.getNamespace(), statement, parameter);
	}

	public int count(final String statement, CriteriaParameter parameter) {
		return this.count(statement, toParameters(parameter));
	}

	/** update */
	public int update(String statement, Map<String, Object> parameter) {
		// updateLifecycleAttribute(model);
		return this.getIbatisDataAccessObject().update(this.getNamespace(), statement, parameter);
	}

	public int update(String statement, CriteriaParameter parameter) {
		// updateLifecycleAttribute(model);
		return this.update(statement, toParameters(parameter));
	}

	/** remove */
	public int remove(String statement, Map<String, Object> parameter) {
		// updateLifecycleAttribute(model);
		return this.getIbatisDataAccessObject().remove(this.getNamespace(), statement, parameter);
	}

	public int remove(String statement, CriteriaParameter parameter) {
		// updateLifecycleAttribute(model);
		return this.remove(statement, toParameters(parameter));
	}

	/**
	 * @param ibatisDataAccessObject
	 *            The ibatisDataAccessObject to set.
	 */
	public void setIbatisDataAccessObject(IbatisDataAccessObject ibatisDataAccessObject) {
		this.ibatisDataAccessObject = ibatisDataAccessObject;
	}

	/**
	 * @return Returns the ibatisDataAccessObject.
	 */
	public IbatisDataAccessObject getIbatisDataAccessObject() {
		return ibatisDataAccessObject;
	}
}
